package com.mth480.rebasedownloader;

import com.biomatters.geneious.publicapi.databaseservice.*;
import com.biomatters.geneious.publicapi.documents.*;
import com.biomatters.geneious.publicapi.plugin.*;
import javax.swing.ImageIcon;
import java.util.*;
import java.io.*;
import org.w3c.dom.*;
//import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.jdom.output.XMLOutputter;


public class RebaseDBService extends DatabaseService {
	
	public Icons getIcons()
	{
		ImageIcon icon = new ImageIcon("dbicon.jpg");
		return new Icons(icon);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.biomatters.geneious.publicapi.databaseservice.DatabaseService#retrieve(com.biomatters.geneious.publicapi.databaseservice.Query, com.biomatters.geneious.publicapi.databaseservice.RetrieveCallback, com.biomatters.geneious.publicapi.documents.URN[])
	 * @summary Called by Geneious whenever a search is started.
	 */
	public DatabaseService.RetrieveStatus retrieve(
			Query query,
            RetrieveCallback callback,
            URN[] urnsToNotRetrieve) 
			throws DocumentRetrieverException
	{
		
		ArrayList<RestrictionEnzymeData> qryData = RunQuery(query.toXML()); 

		for(int i = 0; i < qryData.size(); i++)
		{
			RestrictionEnzyme curr = new RestrictionEnzyme(
					new Date(), qryData.get(i));
			callback.add(curr, null);
		}
		

		return DatabaseService.RetrieveStatus.SUCCESS;
	}
	
	/*
	 * @summary Run a compound query. 
	 * @param t the first childQuery element in the main query
	 * @return returns the data to be changed into documents.
	 */
	public ArrayList<RestrictionEnzymeData> RunQuery(org.jdom.Element t)
	{
		XPathFactory factor = XPathFactory.newInstance();
		XPath xpath = factor.newXPath();
		ArrayList<RestrictionEnzymeData> data = new ArrayList<RestrictionEnzymeData>();
		
		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware (true); // never forget this!
        Document doc;
        try {
        	DocumentBuilder builder = domFactory.newDocumentBuilder();
        	XMLOutputter output = new XMLOutputter();
        	doc = builder.parse(new StringBufferInputStream(output.outputString(t)));
        } catch (Exception e) {
        	e.printStackTrace();
        	return data;
        }
		
		if(t.getName() == "SimpleQuery")
		{
			try {	
				XPathExpression expr = xpath.compile("//SearchOption");
				Object result = expr.evaluate(doc, XPathConstants.NODESET);
				NodeList nodes = (NodeList) result;
				return RebaseDataRetriever.QueryRebaseFile(nodes.item(0).getTextContent(), "", "", "", "", false);
			} catch (Exception e) {
				e.printStackTrace();
				return data;
			}
			
		}
		else 
		{
			String enzymeName = "", prototype = "", microorganism = "", methSiteAndType = "", commercialSource = "";
			boolean andor = false;
			try {
				XPathExpression expr = xpath.compile("//childQuery");
				Object result = expr.evaluate(doc, XPathConstants.NODESET);
				NodeList nodes = (NodeList) result;
				if(nodes.getLength() == 0)
				{
					expr = xpath.compile("//FieldQuery");
					result = expr.evaluate(doc, XPathConstants.NODESET);
					nodes = (NodeList) result;	
					String code = DoQuery(nodes.item(0), "//code");
					String value = DoQuery(nodes.item(0), "//value");
					if(code.equals("RE1")) enzymeName = value;
					else if(code.equals("RE2")) prototype = value;
					else if(code.equals("RE3")) microorganism = value;
					else if(code.equals("RE4")) methSiteAndType = value;
					else if(code.equals("RE5")) commercialSource = value;
					
					//XMLOutputter output = new XMLOutputter();
					//System.out.println(output.outputString(t));
					return RebaseDataRetriever.QueryRebaseFile(enzymeName, prototype, microorganism, methSiteAndType, commercialSource, andor);
				}
				else
				{
					/*
					 * If a query is done for commercial site = 'a' and enzyme name = 'AAGI' the nodes list has two queries, but both
					 * are for the commercial source! Where is the enzyme name??
					 */
					Node curr = nodes.item(0);
					if(curr == null)  return null; // there were no search terms
					String[] searchTerms = getSearchTerms(curr, "//code");
					
					for(int j = 0; j < searchTerms.length; j=j+2)
					{
						if(searchTerms[j].equals("RE1"))
							enzymeName = searchTerms[j+1];
						else if(searchTerms[j].equals("RE2"))
							prototype = searchTerms[j+1];
						else if(searchTerms[j].equals("RE3"))
							microorganism = searchTerms[j+1];
						else if(searchTerms[j].equals("RE4"))
							methSiteAndType = searchTerms[j+1];
						else if(searchTerms[j].equals("RE5"))
							commercialSource = searchTerms[j+1];
					}
						
					String op = DoQuery(doc, "//operator");
					if(op.equals("AND")) andor = true;
				}
				return RebaseDataRetriever.QueryRebaseFile(enzymeName, prototype, microorganism, methSiteAndType, commercialSource, andor);
			} catch (Exception e) {
				e.printStackTrace();
				return data;
			}
		}
	}
	
	
	/*
	 * @summary Returns all the terms selected by the user for a compound search.
	 * @param n the context node to search from
	 * @param xpathString the xpath string to evaluate
	 * @return an array with the pair values for key name, value
	 */
	public String[] getSearchTerms(Node n, String xpathString)
	{	
		Object result;
		// get the nodes for the search terms or values
		XPathFactory factory = XPathFactory.newInstance();
		XPath xpath = factory.newXPath();
		try
		{
			XPathExpression expr = xpath.compile(xpathString);
			result = expr.evaluate(n, XPathConstants.NODESET);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
			
		
		NodeList currNodes = (NodeList) result;
		String[] terms = new String[currNodes.getLength()];
		for(int i = 0; i < (currNodes.getLength() / 2 + 1); i++)
			terms[i] = currNodes.item(i).getTextContent();
		
		String[] searchTermFinal;
		
		if(xpathString.equals("//value"))
			return terms; // the terms are really the values of the terms
		else
		{
			searchTermFinal = new String[terms.length * 2];
			String[] values = getSearchTerms(n, "//value");
			for(int i = 0; i < searchTermFinal.length; i=i+2)
			{
				searchTermFinal[i] = terms[i/2];
				searchTermFinal[i+1] = values[i/2];
			}
		}
		
		return searchTermFinal;
	}
	
	// expects single childQuery node!!!!!!!!!!!
	/*
	 * @summary Gets the value indicated yby the xpathString. 
	 * @param n the context node
	 * @param xpathString the xpath string to evaluate. 
	 */
	public String DoQuery(Node n, String xpathString)
	{
		//System.out.println("1: " + n.getTextContent());
		try{
			// get the factory and create our xpath object
			XPathFactory factory = XPathFactory.newInstance();
			XPath xpath = factory.newXPath();
			//System.out.println("2: " + n.getTextContent());
			XPathExpression expr = xpath.compile(xpathString);
			//String result = xpath.evaluate(xpathString, n);
			Object result = expr.evaluate(n, XPathConstants.NODESET);
			NodeList currNodes = (NodeList) result;
			//System.out.println("Length: " + currNodes.getLength());
			if(currNodes.getLength()==0)return "";
			else return currNodes.item(0).getTextContent();
		} catch (Exception e) {
			e.printStackTrace();
			return "";
		}
	}
	
	
	public String getUniqueID()
	{
		return "RebaseDownloader";
	}
	
	public String getDescription()
	{
		return "Download restriction enzymes from REBASE";
	}
	
	public String getName()
	{
		return "RE Tools";
	}
	
	public String getHelp()
	{
		return "Figure it out for yourself!";
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.biomatters.geneious.publicapi.databaseservice.DatabaseService#getSearchFields()
	 */
	public QueryField[] getSearchFields()
	{
		// These are built in the "RebaseFields" class
		return RebaseFields.searchFields;
		
	}
	 
}
